import React from 'react'

// 导航上下文
const NavigationContext = React.createContext()

// 路径上下文
const LocationContext = React.createContext()

/**
 * 最核心的路由容器组件
 * @param {*} children 子元素 
 * @param {*} location 浏览器地址的完整描述对象 
 * @param {*} navigator 历史对象 也称为导航对象
 */
export function Router({ children, location, navigator }) {
  // 在组件重新渲染时 只要 navigator 的值不发生改变 React.useMemo 不会重新执行回调函数
  // 导航上下文的初始值 { navigator: 导航对象 }
  const navigationValue = React.useMemo(() => ({ navigator }), [navigator])
  const LocationValue = React.useMemo(() => ({ location }), [location])

  return <NavigationContext.Provider value={navigationValue}>
    <LocationContext.Provider value={LocationValue}>
      {children}
    </LocationContext.Provider>
  </NavigationContext.Provider>
}


export function Routes({ children }) {
  const routes = createRoutesFromChildren(children)
  return useRoutes(routes)
}

/**
 * 用于获取浏览器地址的完整描述对象
 * @returns 浏览器地址的完整描述对象
 */
export function useLocation() {
  return React.useContext(LocationContext).location
}

/**
 * 根据路由配置数组渲染对应的组件
 * @param {} routes 
 */
export function useRoutes(routes) {
  const location =  useLocation()
  const pathname = location.pathname || '/'
  for (let i = 0; i < routes.length; i++) {
    const {
      path, // 路由匹配路径
      element, // react 元素
    } = routes[i]
    // 路由匹配路径 与 浏览器地址是否匹配
    const match = matchPath(path, pathname)
    if(match) {
      return element // 如果匹配则直接返回 react 元素
    }
  }

  // 如果所有的路由匹配都无法匹配上 则直接返回 null
  return null
}

/**
 * 匹配路径
 * @param {*} path 匹配的路径
 * @param {*} pathname 浏览器地址栏中的路径
 */
function matchPath(path, pathname) {
  let matcher = compilePath(path)
  let match = pathname.match(matcher)
  return match
}

/**
 * 将 route 中的 path 属性编译成正则表达式
 * @param {*} path 匹配的路径
 */
function compilePath(path) {
  let regexpSource = '^' + path + `$`
  let matcher = new RegExp(regexpSource)
  return matcher
}

/**
 * 是一个 react 函数式组件空实现 里面什么都没有
 */
export function Route() {
  return null
}

/**
 * 根据子元素创建并返回路由配置数组
 */
function createRoutesFromChildren(children) {
  let routes = []
  // 使用 React.Children.forEach 内置方法来遍历每个子元素
  React.Children.forEach(children, (element) => {
    let route = {
      path: element.props.path, // 获取路径
      element: element.props.element, // 获取
    }
    routes.push(route)
  })
  return routes
}
